#include "pch.h"

#include "helper/string.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>


namespace Helper
{

	///
	void * String::operator new(size_t size)
	{
		return Memory::MemoryManager::Alloc(Memory::DataHeap, size);
	}


	///
	void String::operator delete(void *ptr)
	{
		return Memory::MemoryManager::Free(Memory::DataHeap, ptr);
	}





	///
	void String::Delete()
	{
		if(this->m_heapBuffer)
		{
			Memory::MemoryManager::Free(Memory::StringHeap, (void*)this->m_heapBuffer);

			this->m_heapBuffer = 0;
		}

		this->m_localBuffer[0] = 0;
		this->m_strLen = 0;
		this->m_heapBufferSize = 0;
	}


	///
	void String::Alloc(tSize size)
	{
		if(this->m_heapBuffer)
		{
			Memory::MemoryManager::Free(Memory::StringHeap, (void*)this->m_heapBuffer);

			this->m_heapBuffer = 0;
		}

		this->m_heapBuffer = (char*)Memory::MemoryManager::Alloc(Memory::StringHeap, size);

		this->m_heapBufferSize = size;

		this->m_localBuffer[0] = 0;
	}


	///
	void String::Realloc(tSize size)
	{
		char * buffer = (char*)Memory::MemoryManager::Alloc(Memory::StringHeap, size);

		if(this->m_strLen > 0)
		{
			const char * src = this->GetAsCharPtr();

			Memory::MemoryManager::Copy(src, buffer, this->m_strLen);
		}

		buffer[this->m_strLen] = 0;

		if(this->m_heapBuffer)
		{
			Memory::MemoryManager::Free(Memory::StringHeap, (void*)this->m_heapBuffer);

			this->m_heapBuffer = 0;
		}

		this->m_localBuffer[0] = 0;

		this->m_heapBuffer = buffer;

		this->m_heapBufferSize = size;
	}





	///
	String::String():
	m_heapBuffer(0),
		m_strLen(0),
		m_heapBufferSize(0)
	{
		this->m_localBuffer[0] = 0;
	}


	///
	String::String(const char * str):
	m_heapBuffer(0),
		m_strLen(0),
		m_heapBufferSize(0)
	{
		this->m_localBuffer[0] = 0;

		this->SetCharPtr(str);
	}


	///
	String::String(const String & rhs):
	m_heapBuffer(0),
		m_strLen(0),
		m_heapBufferSize(0)
	{
		this->m_localBuffer[0] = 0;

		this->SetCharPtr(rhs.GetAsCharPtr());
	}


	///
	String::~String()
	{
		this->Delete();
	}





	///
	void String::operator =(const Helper::String &rhs)
	{
		if(&rhs != this)
			this->SetCharPtr(rhs.GetAsCharPtr());
	}


	///
	void String::operator =(const char * str)
	{
		this->SetCharPtr(str);
	}


	///
	void String::operator +=(const Helper::String &rhs)
	{
		this->Add(rhs);
	}


	///
	bool operator ==(const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) == 0;
	}


	///
	bool operator == (const String & a, const char * b)
	{
		return strcmp(a.GetAsCharPtr(), b) == 0;
	}


	///
	bool operator ==(const char * a, const String & b)
	{
		return strcmp(a, b.GetAsCharPtr()) == 0;
	}


	///
	bool operator !=(const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) != 0;
	}


	///
	bool operator < (const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) < 0;
	}


	///
	bool operator > (const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) > 0;
	}


	///
	bool operator <= (const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) <= 0;
	}


	///
	bool operator >= (const String & a, const String & b)
	{
		return strcmp(a.GetAsCharPtr(), b.GetAsCharPtr()) >= 0;
	}


	///
	char String::operator [](tIndex i) const
	{
		return this->GetAsCharPtr()[i];
	}


	///
	char & String::operator [](tIndex i)
	{
		return (char&)(this->GetAsCharPtr())[i];
	}





	///
	void String::Reserve(tSize size)
	{
		if(size > this->m_heapBufferSize)
			this->Realloc(size);
	}


	///
	void String::ToUpper()
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());

		char c ;

		while(0 != (c = *str))
		{
			*str++ = (char)toupper(c);
		}
	}


	///
	void String::ToLower()
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());

		char c;

		while(0 != (c = *str))
		{
			*str++ = (char)tolower(c);
		}
	}


	///
	void String::FirstCharToUpper()
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());

		*str = (char)toupper(*str);
	}


	///
	bool String::ContainsCharFromSet(const String & charset) const
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());
		char * ptr = strpbrk(str, charset.GetAsCharPtr());

		return (0 != ptr);
	}





	///
	void String::Add(const Helper::String &str)
	{
		this->Add(str.GetAsCharPtr(), str.m_strLen);
	}


	///
	void String::Add(const char * append, tSize length)
	{
		if(length > 0)
		{
			tSize newlength = this->m_strLen + length;

			if((0 == this->m_heapBuffer) && (newlength < LocalStringSize))
			{
				Memory::MemoryManager::Copy(append, this->m_localBuffer + this->m_strLen, length);

				this->m_localBuffer[newlength] = 0;
			}
			else if(newlength < this->m_heapBufferSize)
			{
				Memory::MemoryManager::Copy(append, this->m_heapBuffer + this->m_strLen, length);

				this->m_heapBuffer[newlength] = 0;
			}
			else
			{
				this->Realloc(newlength + newlength / 2);

				Memory::MemoryManager::Copy(append, this->m_heapBuffer + this->m_strLen, length);

				this->m_heapBuffer[newlength] = 0;
			}

			this->m_strLen = newlength;
		}
	}


	///
	void String::Add(const char * str)
	{
		this->Add(str, (tSize)strlen(str));
	}


	///
	tSize String::Length() const
	{
		return this->m_strLen;
	}


	///
	void String::Clear()
	{
		this->Delete();
	}


	///
	bool String::IsEmpty() const
	{
		return (0 == this->m_strLen);
	}


	///
	bool String::IsValid() const 
	{
		return (0 != this->m_strLen);
	}


	///
	bool String::CopyToBuffer(char *buf, tSize size) const
	{
		HRESULT hr = StringCchCopy(buf, size, this->GetAsCharPtr());

		return SUCCEEDED(hr);
	}


	///
	void __cdecl String::Format(const char *fmt, ...)
	{
		va_list args;

		va_start(args, fmt);

		char buf[4096];

		StringCchVPrintf(buf, sizeof(buf), fmt, args);

		*this = buf;

		va_end(args);
	}


	///
	void __cdecl String::FormatArgList(const char *fmt, va_list args)
	{
		char buf[4096];

		StringCchVPrintf(buf, sizeof(buf), fmt, args);

		*this = buf;
	}





	///
	void String::SetCharPtr(const char *ptr)
	{
		tSize len = 0;

		if(ptr)
		{
			len = (tSize)strlen((const char*)ptr);
		}

		this->Set(ptr, len);
	}


	///
	void String::Set(const char *ptr, tSize length)
	{
		if(0 == ptr)
		{
			this->Delete();

			this->m_localBuffer[0] = 0;
		}
		else if((0 == this->m_heapBuffer) && (length < LocalStringSize))
		{
			this->Delete();

			Memory::MemoryManager::Copy(ptr, this->m_localBuffer, length);

			this->m_localBuffer[length] = 0;
		}
		else if(length < this->m_heapBufferSize)
		{
			Memory::MemoryManager::Copy(ptr, this->m_heapBuffer, length);

			this->m_heapBuffer[length] = 0;

			this->m_localBuffer[0] = 0;
		}
		else
		{
			this->Alloc(length + 1);

			Memory::MemoryManager::Copy(ptr, this->m_heapBuffer, length);

			this->m_heapBuffer[length] = 0;

			this->m_localBuffer[0] = 0;
		}

		this->m_strLen = length;
	}


	///
	void String::SetInt(int val)
	{
		this->Format("%d", val);
	}


	///
	void String::SetFloat(float val)
	{
		this->Format("%.6f", val);
	}


	///
	void String::SetBool(bool val)
	{
		if(val)
			this->SetCharPtr("true");
		else 
			this->SetCharPtr("false");
	}


	///
	void String::SetFloat2(const Math::float2 & val)
	{
		this->Format("%.6f,%.6f",
			val.getx(),
			val.gety());
	}


	///
	void String::SetFloat4(const Math::float4 &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.getx(),
			val.gety(),
			val.getz(),
			val.getw());
	}


	///
	void String::SetMatrix(const Math::matrix &val)
	{
		this->Format("%.6f,%.f6,%.6f,%.6f,%.6f,%.f6,%.6f,%.6f,%.6f,%.f6,%.6f,%.6f,%.6f,%.f6,%.6f,%.6f",
			val.getr1().getx(), val.getr1().gety(), val.getr1().getz(), val.getr1().getw(),
			val.getr2().getx(), val.getr2().gety(), val.getr2().getz(), val.getr2().getw(),
			val.getr3().getx(), val.getr3().gety(), val.getr3().getz(), val.getr3().getw(),
			val.getr4().getx(), val.getr4().gety(), val.getr4().getz(), val.getr4().getw());
	}


	///
	void String::SetFloatRect(const Math::rectangle<float> &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.m_left,
			val.m_top,
			val.m_right,
			val.m_bottom);
	}


	///
	void String::SetIntRect(const Math::rectangle<int> &val)
	{
		this->Format("%d,%d,%d,%d",
			val.m_left,
			val.m_top,
			val.m_right,
			val.m_bottom);
	}


	///
	void String::SetLine2(const Math::line2 &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.getstart().getx(),
			val.getstart().gety(),
			val.getend().getx(),
			val.getend().gety());
	}


	///
	void String::SetLine3(const Math::line3 &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f,%.6f,%.6f",
			val.getstart().getx(),
			val.getstart().gety(),
			val.getstart().getz(),
			val.getend().getx(),
			val.getend().gety(),
			val.getend().getz());
	}


	///
	void String::SetPlane(const Math::plane &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.getA(),
			val.getB(),
			val.getC(),
			val.getD());
	}


	///
	void String::SetPoint2(const Math::point2 &val)
	{
		this->Format("%.6f,%.6f",
			val.getx(),
			val.gety());
	}


	///
	void String::SetPoint4(const Math::point4 &val)
	{
		this->Format("%.6f,%.6f,%.6f",
			val.getx(),
			val.gety(),
			val.getz());
	}


	///
	void String::SetQuaternion(const Math::quaternion &val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.getx(),
			val.gety(),
			val.getz(),
			val.getw());
	}


	///
	void String::SetVector2(const Math::vector2 &val)
	{
		this->Format("%.6f,%.6f",
			val.getx(),
			val.gety());
	}


	///
	void String::SetVector3(const Math::vector3 &val)
	{
		this->Format("%.6f,%.6f,%.6f",
			val.getx(),
			val.gety(),
			val.getz());
	}


	///
	void String::SetVector4(const Math::vector4 & val)
	{
		this->Format("%.6f,%.6f,%.6f,%.6f",
			val.getx(),
			val.gety(),
			val.getz(),
			val.getw());
	}





	///
	const char * String::GetAsCharPtr() const
	{
		if(this->m_heapBuffer)
			return this->m_heapBuffer;
		else
			return this->m_localBuffer;
	}


	///
	int String::GetAsInt() const
	{
		return atoi(this->GetAsCharPtr());
	}


	///
	float String::GetAsFloat() const
	{
		return float(atof(this->GetAsCharPtr()));
	}


	///
	bool String::GetAsBool() const 
	{
		static const char * bools[] = { "no", "yes", "off", "on", "false", "true", 0 };

		tIndex i = 0;

		while(bools[i] != 0)
		{
			if(0 == _stricmp(bools[i], this->GetAsCharPtr()))
			{
				return 1 == (i & 1);
			}

			i++;
		}

		return false;
	}


	///
	Math::float2 String::GetAsFloat2() const
	{
		Array<String> tokens(0, 2);

		this->Tokenize(", \t", tokens);

		return Math::float2(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat());
	}


	///
	Math::float4 String::GetAsFloat4() const
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::float4(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat());
	}


	///
	Math::rectangle<float> String::GetAsFloatRect() const
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::rectangle<float>(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat());
	}


	///
	Math::rectangle<int> String::GetAsIntRect() const
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::rectangle<int>(tokens[0].GetAsInt(),
			tokens[1].GetAsInt(),
			tokens[2].GetAsInt(),
			tokens[3].GetAsInt());
	}


	///
	Math::line2 String::GetAsLine2() const
	{
		Array<String> tokens(0,4);

		this->Tokenize(", \t", tokens);

		return Math::line2(Math::point2(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat()),
			Math::point2(tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat()));
	}


	///
	Math::line3 String::GetAsLine3() const
	{
		Array<String> tokens(0, 6);

		this->Tokenize(", \t", tokens);

		return Math::line3(Math::point4(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat()),
			Math::point4(tokens[3].GetAsFloat(),
			tokens[4].GetAsFloat(),
			tokens[5].GetAsFloat()));
	}


	///
	Math::matrix String::GetAsMatrix() const
	{
		Array<String> tokens(0, 16);

		this->Tokenize(", \t", tokens);

		return Math::matrix(Math::float4(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat()),
			Math::float4(tokens[4].GetAsFloat(),
			tokens[5].GetAsFloat(),
			tokens[6].GetAsFloat(),
			tokens[7].GetAsFloat()),
			Math::float4(tokens[8].GetAsFloat(),
			tokens[9].GetAsFloat(),
			tokens[10].GetAsFloat(),
			tokens[11].GetAsFloat()),
			Math::float4(tokens[12].GetAsFloat(),
			tokens[13].GetAsFloat(),
			tokens[14].GetAsFloat(),
			tokens[15].GetAsFloat()));
	}


	///
	Math::plane String::GetAsPlane() const
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::plane(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat());
	}


	///
	Math::point2 String::GetAsPoint2() const 
	{
		Array<String> tokens(0, 2);

		this->Tokenize(", \t", tokens);

		return Math::point2(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat());
	}


	///
	Math::point4 String::GetAsPoint4() const
	{
		Array<String> tokens(0, 3);

		this->Tokenize(", \t", tokens);

		return Math::point4(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat());
	}


	///
	Math::quaternion String::GetAsQuaternion() const
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::quaternion(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat());
	}


	///
	Math::vector2 String::GetAsVector2() const
	{
		Array<String> tokens(0, 2);

		this->Tokenize(", \t", tokens);

		return Math::vector2(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat());
	}


	///
	Math::vector3 String::GetAsVector3() const
	{
		Array<String> tokens(0, 3);

		this->Tokenize(", \t", tokens);

		return Math::vector3(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat());
	}


	///
	Math::vector4 String::GetAsVector4() const 
	{
		Array<String> tokens(0, 4);

		this->Tokenize(", \t", tokens);

		return Math::vector4(tokens[0].GetAsFloat(),
			tokens[1].GetAsFloat(),
			tokens[2].GetAsFloat(),
			tokens[3].GetAsFloat());
	}





	bool String::IsValidBool() const
	{
		static const char * bools[] = { "no", "yes", "off", "on", "false", "true", 0 };

		tIndex i = 0;

		while(bools[i] != 0)
		{
			if(0 == _stricmp(bools[i], this->GetAsCharPtr()))
			{
				return true;
			}

			i++;
		}

		return false;
	}


	///
	bool String::IsValidInt() const 
	{
		return this->CheckValidCharSet(" \t-+0123456789");
	}


	///
	bool String::IsValidFloat() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidFloat2() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidFloat4() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidFloatRect() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidIntRect() const 
	{
		return this->CheckValidCharSet(" \t-+,1234567890");
	}


	///
	bool String::IsValidLine2() const
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidLine3() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidMatrix() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidPlane() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidPoint2() const
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidPoint4() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidQuaternion() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidVector2() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidVector3() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}


	///
	bool String::IsValidVector4() const 
	{
		return this->CheckValidCharSet(" \t-+.,e1234567890");
	}





	///
	String String::FromInt(int val)
	{
		String result;

		result.SetInt(val);

		return result;
	}


	///
	String String::FromBool(bool val)
	{
		String result;

		result.SetBool(val);

		return result;
	}


	///
	String String::FromFloat(float val)
	{
		String result;

		result.SetFloat(val);

		return result;
	}


	///
	String String::FromFloat2(const Math::float2 & val)
	{
		String result;

		result.SetFloat2(val);

		return result;
	}


	///
	String String::FromFloat4(const Math::float4 &val)
	{
		String result;

		result.SetFloat4(val);

		return result;
	}


	///
	String String::FromFloatRect(const Math::rectangle<float> & val)
	{
		String result;

		result.SetFloatRect(val);

		return result;
	}


	///
	String String::FromIntRect(const Math::rectangle<int> &val)
	{
		String result;

		result.SetIntRect(val);

		return result;
	}


	///
	String String::FromLine2(const Math::line2 & val)
	{
		String result;

		result.SetLine2(val);

		return result;
	}


	///
	String String::FromLine3(const Math::line3 & val)
	{
		String result;

		result.SetLine3(val);

		return result;
	}


	///
	String String::FromMatrix(const Math::matrix &val)
	{
		String result;

		result.SetMatrix(val);

		return result;
	}


	///
	String String::FromPlane(const Math::plane & val)
	{
		String result;

		result.SetPlane(val);

		return result;
	}


	///
	String String::FromPoint2(const Math::point2 & val)
	{
		String result;

		result.SetPoint2(val);

		return result;
	}


	///
	String String::FromPoint4(const Math::point4 & val)
	{
		String result;

		result.SetPoint4(val);

		return result;
	}


	///
	String String::FromQuaternion(const Math::quaternion &val)
	{
		String result;

		result.SetQuaternion(val);

		return result;
	}


	///
	String String::FromVector2(const Math::vector2 & val)
	{
		String result;

		result.SetVector2(val);

		return result;
	}


	///
	String String::FromVector3(const Math::vector3 & val)
	{
		String result;

		result.SetVector3(val);

		return result;
	}


	///
	String String::FromVector4(const Math::vector4 & val)
	{
		String result;

		result.SetVector4(val);

		return result;
	}





	///
	void String::AddBool(bool val)
	{
		this->Add(String::FromBool(val));
	}


	///
	void String::AddFloat(float val)
	{
		this->Add(String::FromFloat(val));
	}


	///
	void String::AddFloat2(const Math::float2 &val)
	{
		this->Add(String::FromFloat2(val));
	}


	///
	void String::AddFloat4(const Math::float4 &val)
	{
		this->Add(String::FromFloat4(val));
	}


	///
	void String::AddFloatRect(const Math::rectangle<float> &val)
	{
		this->Add(String::FromFloatRect(val));
	}


	///
	void String::AddInt(int val)
	{
		this->Add(String::FromInt(val));
	}


	///
	void String::AddIntRect(const Math::rectangle<int> & val)
	{
		this->Add(String::FromIntRect(val));
	}


	///
	void String::AddLine2(const Math::line2 & val)
	{
		this->Add(String::FromLine2(val));
	}


	///
	void String::AddLine3(const Math::line3 & val)
	{
		this->Add(String::FromLine3(val));
	}


	///
	void String::AddMatrix(const Math::matrix & val)
	{
		this->Add(String::FromMatrix(val));
	}


	///
	void String::AddPlane(const Math::plane & val)
	{
		this->Add(String::FromPlane(val));
	}


	///
	void String::AddPoint2(const Math::point2 & val)
	{
		this->Add(String::FromPoint2(val));
	}


	///
	void String::AddPoint4(const Math::point4 & val)
	{
		this->Add(String::FromPoint4(val));
	}


	///
	void String::AddQuaternion(const Math::quaternion & val)
	{
		this->Add(String::FromQuaternion(val));
	}


	///
	void String::AddVector2(const Math::vector2 &val)
	{
		this->Add(String::FromVector2(val));
	}


	///
	void String::AddVector3(const Math::vector3 & val)
	{
		this->Add(String::FromVector3(val));
	}


	///
	void String::AddVector4(const Math::vector4 & val)
	{
		this->Add(String::FromVector4(val));
	}





	///
	tIndex String::HashCode() const 
	{
		tIndex hash = 0;

		const char * ptr = this->GetAsCharPtr();

		tSize len = this->m_strLen;

		for(tIndex i = 0; i < len; i++)
		{
			hash += ptr[i];
			hash += hash << 10;
			hash ^= hash >> 6;
		}

		hash += hash << 3;
		hash ^= hash >> 11;
		hash += hash << 15;
		hash &= ~(1<<31);

		return hash;
	}


	///
	void String::ReplaceChar(char c, char sub)
	{
		char * ptr = const_cast<char*>(this->GetAsCharPtr());

		for(tIndex i = 0; i <= this->m_strLen; i++)
		{
			if(ptr[i] == c)
			{
				ptr[i] = sub;
			}
		}
	}


	///
	void String::ReplaceChars(const Helper::String &charset, char replacement)
	{
		char * ptr = const_cast<char*>(this->GetAsCharPtr());

		char c;

		while(0 != (c = *ptr))
		{
			if(strchr(charset.GetAsCharPtr(), c))
			{
				*ptr = replacement;
			}

			ptr++;
		}
	}


	///
	void String::ReplaceString(const Helper::String &str, const Helper::String &sub)
	{
		const char * ptr = this->GetAsCharPtr();

		tSize matchStrLen = str.m_strLen;

		String dst;

		const char * occur;

		while(0 != (occur = strstr(ptr, str.GetAsCharPtr())))
		{
			dst.Add(ptr, tSize(occur - ptr));

			if(sub.Length() > 0)
			{
				dst.Add(sub);
			}

			ptr = occur + matchStrLen;
		}

		dst.Add(ptr);

		*this = dst;
	}


	///
	void String::ReplaceIllegalFilenameChars(char replacement)
	{
		this->ReplaceChars("\\/:*?\"<>|", replacement);
	}


	///
	void String::ConvertBackslashes()
	{
		this->ReplaceChar('\\', '/');
	}


	///
	bool String::CheckValidCharSet(const Helper::String &charset) const
	{
		for(tIndex i = 0; i < this->m_strLen; i++)
		{
			if(InvalidIndex == charset.FindCharIndex((*this)[i], 0))
			{
				return false;
			}
		}

		return true;
	}


	///
	tSize String::Tokenize(const Helper::String &space, Helper::Array<String> &outTokens) const
	{
		outTokens.Clear();

		String str(*this);

		char * ptr = const_cast<char*>(str.GetAsCharPtr());

		const char * token;

		while(0 != (token = strtok(ptr, space.GetAsCharPtr())))
		{
			outTokens.Add(token);

			ptr = 0;
		}

		return outTokens.Size();
	}


	///
	Array<String> String::Tokenize(const String & space) const 
	{
		Array<String> tokens;

		this->Tokenize(space, tokens);

		return tokens;
	}


	///
	tSize String::Tokenize(const Helper::String &space, char fence, Helper::Array<String> &outTokens) const
	{
		outTokens.Clear();

		String str(*this);

		char * ptr = const_cast<char*>(str.GetAsCharPtr());

		char * end = ptr + strlen(ptr);

		while(ptr < end)
		{
			char * c;

			while(*ptr && strchr(space.GetAsCharPtr(), *ptr))
			{
				ptr++;
			}

			if(*ptr)
			{
				if((fence == *ptr) && (0 != (c = strchr(++ptr, fence))))
				{
					*c++ = 0;
					outTokens.Add(ptr);
					ptr = c;
				}
				else if(0 != strpbrk(ptr, space.GetAsCharPtr()))
				{
					*c++ = 0;
					outTokens.Add(ptr);
					ptr = c;
				}
				else
				{
					outTokens.Add(ptr);

					break;
				}
			}
		}

		return outTokens.Size();
	}


	///
	Array<String> String::Tokenize(const Helper::String &space, char fence) const
	{
		Array<String> result;

		this->Tokenize(space, fence, result);

		return result;
	}


	///
	String String::ExtractRange(tIndex from, tSize chars) const
	{
		const char * str = this->GetAsCharPtr();

		String result;

		result.Set(&(str[from]), chars);

		return result;
	}


	///
	String String::ExtractToEnd(tIndex from) const 
	{
		return this->ExtractRange(from, this->m_strLen - from);
	}


	///
	void String::Strip(const String & charset)
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());
		char * ptr = strpbrk(str, charset.GetAsCharPtr());

		if(ptr)
		{
			*ptr = 0;
		}

		this->m_strLen = (tSize)strlen(this->GetAsCharPtr());
	}


	///
	tIndex String::FindStringIndex(const String & str, tIndex start) const 
	{
		for(tIndex i = start; i < this->m_strLen; i++)
		{
			if((this->m_strLen - 1) < str.m_strLen)
			{
				break;
			}

			if(strncmp(&(this->GetAsCharPtr()[i]), str.GetAsCharPtr(), str.m_strLen) == 0)
			{
				return i;
			}
		}

		return InvalidIndex;
	}


	///
	tIndex String::FindCharIndex(char c, tIndex start) const
	{
		if(this->m_strLen > 0)
		{
			const char * ptr = strchr(this->GetAsCharPtr() + start, c);

			if(ptr)
			{
				return tIndex(ptr - this->GetAsCharPtr());
			}
		}

		return InvalidIndex;
	}


	///
	void String::TrimLeft(const Helper::String &charset)
	{
		if(this->IsValid())
		{
			tSize charSetLen = charset.m_strLen;
			tIndex thisI = 0;

			bool stopped = false;

			while(!stopped && (thisI < this->m_strLen))
			{
				tIndex charSetI;

				bool match = false;

				for(charSetI = 0; charSetI < charSetLen; charSetI++)
				{
					if((*this)[thisI] == charset[charSetI])
					{
						match = true;

						break;
					}
				}
				if(!match)
				{
					stopped = true;
				}
				else
				{
					++thisI;
				}
			}

			String trimmed(&(this->GetAsCharPtr()[thisI]));

			*this = trimmed;
		}
	}


	///
	void String::TrimRight(const Helper::String &charset)
	{
		if(this->IsValid())
		{
			tSize charSetLen = charset.m_strLen;

			int thisI = this->m_strLen - 1;

			bool stopped = false;

			while(!stopped && (thisI >= 0))
			{
				tIndex charSetI;

				bool match = false;

				for(charSetI = 0; charSetI < charSetLen; charSetI++)
				{
					if((*this)[thisI] == charset[charSetI])
					{
						match = true;

						break;
					}
				}
				if(!match)
				{
					stopped = true;
				}
				else
				{
					--thisI;
				}
			}

			String trimmed;

			trimmed.Set(this->GetAsCharPtr(), thisI + 1);

			*this = trimmed;
		}

	}


	///
	void String::Trim(const Helper::String &charset)
	{
		this->TrimLeft(charset);

		this->TrimRight(charset);
	}


	///
	void String::UTF8ToANSI()
	{
		unsigned char* src = (unsigned char*) this->GetAsCharPtr();
		unsigned char* dst = src;
		unsigned char c;
		while (0 != (c = *src++))
		{
			if (c >= 0x80)
			{
				if ((c & 0xE0) == 0xC0)
				{
					// a 2 byte sequence with 11 bits of information
					ushort wide = ((c & 0x1F) << 6) | (*src++ & 0x3F);
					if (wide > 0xff)
					{
						c = '?';
					}
					else
					{
						c = (unsigned char) wide;
					}
				}
				else if ((c & 0xF0) == 0xE0)
				{
					// a 3 byte sequence with 16 bits of information
					c = '?';
					src += 2;
				}
				else if ((c & 0xF8) == 0xF0)
				{
					// a 4 byte sequence with 21 bits of information
					c = '?';
					src += 3;
				}
				else if ((c & 0xFC) == 0xF8)
				{
					// a 5 byte sequence with 26 bits of information
					c = '?';
					src += 4;
				}
				else if ((c & 0xFE) == 0xFC)
				{
					// a 6 byte sequence with 31 bits of information
					c = '?';
					src += 5;
				}
			}
			*dst++ = c;
		}
		*dst = 0;
	}


	///
	void String::ANSIToUTF8()
	{
		tSize bufSize = this->m_strLen * 2 + 1;

		char* buffer = (char*) Memory::MemoryManager::Alloc(Memory::ScratchHeap, bufSize);
		char* dstPtr = buffer;
		const char* srcPtr = this->GetAsCharPtr();
		unsigned char c;
		while (0 != (c = *srcPtr++))
		{
			// note: this only covers the 2 cases that the character
			// is between 0 and 127 and between 128 and 255
			if (c < 128)
			{
				*dstPtr++ = c;
			}
			else
			{
				*dstPtr++ = 192 + (c / 64);
				*dstPtr++ = 128 + (c % 64);
			}
		}
		*dstPtr = 0;
		this->SetCharPtr(buffer);
		Memory::MemoryManager::Free(Memory::ScratchHeap, buffer);
	}


	///
	bool String::MatchPattern(const Helper::String &string, const Helper::String &pattern)
	{
		const char* str = string.GetAsCharPtr();
		const char* pat = pattern.GetAsCharPtr();

		char c2;
		bool always = true;
		while (always != false)
		{
			if (*pat == 0) 
			{
				if (*str == 0) return true;
				else           return false;
			}
			if ((*str == 0) && (*pat != '*')) return false;
			if (*pat=='*') 
			{
				pat++;
				if (*pat==0) return true;
				while (always)
				{
					if (String::MatchPattern(str, pat)) return true;
					if (*str==0) return false;
					str++;
				}
			}
			if (*pat=='?') goto match;
			if (*pat=='[') 
			{
				pat++;
				while (always) 
				{
					if ((*pat==']') || (*pat==0)) return false;
					if (*pat==*str) break;
					if (pat[1] == '-') 
					{
						c2 = pat[2];
						if (c2==0) return false;
						if ((*pat<=*str) && (c2>=*str)) break;
						if ((*pat>=*str) && (c2<=*str)) break;
						pat+=2;
					}
					pat++;
				}
				while (*pat!=']') 
				{
					if (*pat==0) 
					{
						pat--;
						break;
					}
					pat++;
				}
				goto match;
			}

			if (*pat=='\\') 
			{
				pat++;
				if (*pat==0) return false;
			}
			if (*pat!=*str) return false;

match:
			pat++;
			str++;
		}
		// can't happen
		return false;
	}


	///
	String String::Concatenate(const Helper::Array<String> &strarray, const Helper::String &space)
	{
		String res;

		res.Reserve(256);

		tIndex i;

		tSize num = strarray.Size();

		for(i = 0; i < num; i++)
		{
			res.Add(strarray[i]);

			if((i + 1) < num)
			{
				res.Add(space);
			}
		}

		return res;
	}


	///
	void String::TerminateAtIndex(tIndex index)
	{
		char * ptr = const_cast<char*>(this->GetAsCharPtr());

		ptr[index] = 0;

		this->m_strLen = (tSize)strlen(ptr);
	}






	///
	bool String::CheckFileExtension(const String & ext) const 
	{
		return (this->GetFileExtension() == ext);
	}


	///
	String String::ExtractFileName() const 
	{
		String result;

		const char * slash = this->GetLastSlash();

		if(slash)
		{
			result = &(slash[1]);
		}
		else
		{
			result = *this;
		}

		return result;
	}


	///
	String String::ExtractToLastSlash() const 
	{
		String result(*this);

		char * slash = result.GetLastSlash();

		if(slash)
		{
			slash[1] = 0;
		}
		else
		{
			result = "";
		}

		return result;
	}


	///
	String String::ExtractLastDirName() const 
	{
		String path(*this);

		char * slash = path.GetLastSlash();

		if(slash)
		{
			if(0 == slash[1])
			{
				*slash = 0;
				slash = path.GetLastSlash();
			}

			char * secSlash = 0;

			if(0 != slash)
			{
				*slash = 0;
				secSlash = path.GetLastSlash();

				if(secSlash)
				{
					*secSlash = 0;

					return String(secSlash + 1);
				}
			}
		}

		return "";
	}


	///
	String String::ExtractDirName() const
	{
		String result(*this);

		char * slash = result.GetLastSlash();

		if(slash)
		{
			if(0 == slash[1])
			{
				*slash = 0;

				slash = result.GetLastSlash();
			}

			if(slash)
			{
				*++slash = 0;
			}
		}

		result.m_strLen = (tSize)strlen(result.GetAsCharPtr());

		return result;
	}


	///
	void String::StripFileExtension()
	{
		char * str = const_cast<char*>(this->GetAsCharPtr());
		char * ext = strrchr(str, '.');

		if(ext)
		{
			*ext = 0;

			this->m_strLen = (tSize)strlen(this->GetAsCharPtr());
		}
	}


	///
	String String::GetFileExtension() const 
	{
		const char * str = this->GetAsCharPtr();

		const char * ext = strrchr(str, '.');

		if(ext)
		{
			ext++;

			return String(ext);
		}

		return String("");
	}


	///
	char * String::GetLastSlash() const 
	{
		const char * str = this->GetAsCharPtr();

		const char * slash = strrchr(str, '/');

		if(0 == slash)
			slash = strrchr(str, '\\');
		if(0 == slash)
			slash = strrchr(str, ':');

		return const_cast<char*>(slash);
	}





	///
	bool String::IsDigit(char c)
	{
		return (0 != isdigit(int(c)));
	}


	///
	bool String::IsAlpha(char c)
	{
		return (0 != isalpha(int(c)));
	}


	///
	bool String::IsAlNum(char c)
	{
		return (0 != isalnum(int(c)));
	}


	///
	bool String::IsLower(char c)
	{
		return (0 != islower(int(c)));
	}


	///
	bool String::IsUpper(char c)
	{
		return (0 != isupper(int(c)));
	}


	///
	int String::StrCmp(const char * a, const char * b)
	{
		return strcmp(a, b);
	}


	///
	int String::StrLen(const char *str)
	{
		return strlen(str);
	}


	///
	const char * String::StrChr(const char * str, int c)
	{
		return strchr(str, c);
	}

}